/*
 * HudVScale.fx
 */

package org.frapwidgets.scene.control.hud;

import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.control.Behavior;
import javafx.scene.control.Control;
import javafx.scene.control.Skin;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Panel;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import javafx.util.Math.*;
import org.jfxtras.scene.layout.MigLayout;
import org.jfxtras.scene.layout.MigLayout.*;

import org.frapwidgets.scene.control.data.RangeData;



/**
 * @author Jonathan Merritt
 */

public class HudVScale extends Control {

  public var range: RangeData = RangeData { };

  override function create(): Node {
    skin = HudVScaleSkin { };
    super.create();
  }

}


public class HudVScaleSkin extends Skin {
  public var vscaleControl = bind control as HudVScale;
  override var behavior = HudVScaleBehavior { }

  def layoutTextTemplate = Text {
    content: "0";
    visible: false
  }
  def txtHeight = bind layoutTextTemplate.layoutBounds.height;

  def range = bind vscaleControl.range;
  def min: Double = bind range.min;
  def max: Double = bind range.max;
  def value: Double = bind range.value on replace {
    makeTicksAndLabels();
  };
  def extent: Double = bind range.extent;
  def compHeight: Double = bind vscaleControl.height;

  var tickSize: Number;
  // when the extent, text height, or component height change, we have to
  //  re-compute the tick size
  def boundExtentTxtHeight = bind [ extent, txtHeight, compHeight ] on replace {
    // find the lower bound for the tick size, based upon component dimensions,
    //  label height and extent
    def nVertLabels = compHeight / txtHeight;
    def tickSizeLowerBound: Double = extent / nVertLabels;

    // find the lower bound on the tick size in mantissa-exponent form
    def exp = pow(10.0, floor(log10(tickSizeLowerBound)));
    def mant = if (exp != 0.0) tickSizeLowerBound / exp else 0.0;

    // find a mantissa for the tick size from an allowed list that is just
    //  larger than the minimum tick size mantissa; adjust exponent if
    //  necessary
    var tickExp: Number = exp;
    def tickMant = if (mant < 0.125) {
      0.125;
    } else if (mant < 0.25) {
      0.25;
    } else if (mant < 0.5) {
      0.5;
    } else {
      tickExp *= 10;
      1;
    }

    // set the tick size
    tickSize = tickExp * tickMant;

    makeTicksAndLabels();
  }

  var labels: Node;
  var ticks: Node;

  function makeTicksAndLabels(): Void {
    def minTick = floor(value / tickSize) * tickSize;
    def maxTick = ceil((value + extent) / tickSize) * tickSize;
    def nTicks = (maxTick - minTick) / tickSize;
    def tickLines = for (i in [0 .. nTicks]) {
      def tickValue = minTick + i * tickSize;
      def t = (1.0 - ((tickValue - value) / extent)) * (compHeight);
      Line {
        startX: 0
        endX: bind scrollRegion.width
        startY: t, endY: t
        stroke: Color.WHITE
      }
    }
    def tickLabels = for (i in [0 .. nTicks]) {
      def tickValue = minTick + i * tickSize;
      def t = (1.0 - ((tickValue - value) / extent)) * (compHeight);
      def txt:Text = Text {
        fill: Color.WHITE
        content: tickValue.toString()
        layoutX: bind scaleRegion.width - txt.layoutBounds.width;
        layoutY: bind txt.layoutBounds.height / 2 + t;
      }
      txt
    }
    ticks = Group {
      content: tickLines
    }
    labels = Group {
      content: tickLabels
    }
  }

  def scrollRegion: Panel = Panel {
    content: bind [
      Rectangle {
        width: bind scrollRegion.width
        height: bind scrollRegion.height
        fill: Color.color(0.3, 0.3, 0.3)
      },
      ticks
    ]
    var mouseY: Number;
    onMousePressed: function(m: MouseEvent) {
      mouseY = m.screenY;
    }
    onMouseDragged: function(m: MouseEvent) {
      def dy = m.screenY - mouseY;
      mouseY = m.screenY;
      def dValue = dy * extent / compHeight;
      vscaleControl.range.value += dValue;
    }
  }

  def scaleRegion: Panel = Panel {
    content: bind [
      Rectangle {
        width: bind scaleRegion.width
        height: bind scaleRegion.height
        fill: Color.color(0.1, 0.1, 0.1)
      },
      labels
    ]
    var center: Number;
    var mouseY: Number;
    onMousePressed: function(m: MouseEvent) {
      mouseY = m.screenY;
      center = m.y / compHeight;
    }
    onMouseDragged: function(m: MouseEvent) {
      def dy = m.screenY - mouseY;
      mouseY = m.screenY;
      def dZoom = 0.005 * dy;
      vscaleControl.range.extent += vscaleControl.range.extent * dZoom;
      vscaleControl.range.value -= dZoom * (1.0 - center) * extent;
    }
  }

  def controlNode = MigLayout {
    width: bind vscaleControl.width
    height: bind vscaleControl.height
    constraints: "fill, gap 0, insets 0"
    columns: "[fill][20!]"
    rows: "[fill]"
    content: [
      migNode( scaleRegion, "grow" ),
      migNode( scrollRegion, "growy, width 20!" )
      /* migNode( layoutTextTemplate, "pos 0 0" ) */
    ]
  }


  init {
    node = controlNode;
  }

  override function contains(localX: Number, localY: Number): Boolean {
    node.contains(localX, localY)
  }

  override function intersects(
    localX: Number,
    localY: Number,
    localWidth: Number,
    localHeight: Number
  ): Boolean {
    node.intersects(localX, localY, localWidth, localHeight)
  }
}


public class HudVScaleBehavior extends Behavior {

}
